﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;

namespace CC.UI
{
    /// <summary>
    /// 拆分容器
    /// 创建人：梁宏基
    /// 创建时间：2020-12-30
    /// </summary>
    public class SplitContainer : System.Windows.Forms.SplitContainer
    {
        private const int ToggleLength = 50;
        private readonly PropertyInfo mOverrideCursor;
        private Padding mOriginalPadding = Padding.Empty;
        private bool mShowToggleButton = true;
        private FixedPanel mToggleButtonHover = FixedPanel.None;

        public SplitContainer()
        {
            mOverrideCursor = GetPrivateProperty("OverrideCursor");
            Panel1.SizeChanged += Panel_SizeChanged;
            Panel2.SizeChanged += Panel_SizeChanged;
            Panel1.Paint += Panel_Paint;
            Panel2.Paint += Panel_Paint;
            Panel1.MouseClick += Panel_MouseClick;
            Panel2.MouseClick += Panel_MouseClick;
            Panel1.MouseMove += Panel_MouseMove;
            Panel2.MouseMove += Panel_MouseMove;
            Panel1.MouseLeave += Panel_MouseLeave;
            Panel2.MouseLeave += Panel_MouseLeave;
        }

        /// <summary>
        /// 获取或设置是否显示开关按钮
        /// </summary>
        [DefaultValue(true)]
        public bool ShowToggleButton
        {
            get { return mShowToggleButton; }
            set
            {
                mShowToggleButton = value;
                Invalidate();
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            DrawToggleButton(this, e.Graphics);
            RaisePaintEvent(this, e);
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            if (mShowToggleButton)
                Invalidate();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (HandleMouseMove(this, e.Location))
                return;
            base.OnMouseMove(e);
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            HandleMouseLeave(this);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (!DesignMode && mShowToggleButton)
            {
                if (ToggleRect.Contains(e.Location))
                    return;
            }
            base.OnMouseDown(e);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (!DesignMode && mShowToggleButton)
            {
                if (ToggleRect.Contains(e.Location))
                    return;
            }
            base.OnMouseUp(e);
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            if (!DesignMode && mShowToggleButton)
            {
                Rectangle toggleRect = ToggleRect;
                if (toggleRect.Contains(e.Location))
                {
                    mToggleButtonHover = FixedPanel.None;
                    if (Orientation == Orientation.Horizontal)
                    {
                        if (e.Location.X < toggleRect.X + toggleRect.Width / 2)
                        {
                            mOriginalPadding = Panel2.Padding;
                            Panel2.Padding = new Padding(mOriginalPadding.Left, mOriginalPadding.Top > SplitterWidth ? mOriginalPadding.Top : SplitterWidth, mOriginalPadding.Right, mOriginalPadding.Bottom);
                            Panel1Collapsed = true;
                        }
                        else
                        {
                            mOriginalPadding = Panel1.Padding;
                            Panel1.Padding = new Padding(mOriginalPadding.Left, mOriginalPadding.Top, mOriginalPadding.Right, mOriginalPadding.Bottom > SplitterWidth ? mOriginalPadding.Bottom : SplitterWidth);
                            Panel2Collapsed = true;
                        }
                    }
                    else
                    {
                        if (e.Location.Y < toggleRect.Y + toggleRect.Height / 2)
                        {
                            mOriginalPadding = Panel2.Padding;
                            Panel2.Padding = new Padding(mOriginalPadding.Left > SplitterWidth ? mOriginalPadding.Left : SplitterWidth, mOriginalPadding.Top, mOriginalPadding.Right, mOriginalPadding.Bottom);
                            Panel1Collapsed = true;
                        }
                        else
                        {
                            mOriginalPadding = Panel1.Padding;
                            Panel1.Padding = new Padding(mOriginalPadding.Left, mOriginalPadding.Top, mOriginalPadding.Right > SplitterWidth ? mOriginalPadding.Right : SplitterWidth, mOriginalPadding.Bottom);
                            Panel2Collapsed = true;
                        }
                    }
                    return;
                }
            }
            base.OnMouseClick(e);
        }

        private void Panel_SizeChanged(object sender, EventArgs e)
        {
            if (mShowToggleButton)
                ((Control)sender).Invalidate();
        }

        private void Panel_Paint(object sender, PaintEventArgs e)
        {
            DrawToggleButton((Control)sender, e.Graphics);
        }

        private void Panel_MouseClick(object sender, MouseEventArgs e)
        {
            if (DesignMode || e.Button != MouseButtons.Left || !ToggleRect.Contains(e.Location))
                return;
            mToggleButtonHover = FixedPanel.None;
            if (Panel1Collapsed)
            {
                Panel2.Padding = mOriginalPadding;
                Panel1Collapsed = false;
            }
            else if (Panel2Collapsed)
            {
                Panel1.Padding = mOriginalPadding;
                Panel2Collapsed = false;
            }
        }

        private void Panel_MouseMove(object sender, MouseEventArgs e)
        {
            HandleMouseMove((Control)sender, e.Location);
        }

        private void Panel_MouseLeave(object sender, EventArgs e)
        {
            HandleMouseLeave((Control)sender);
        }

        private bool HandleMouseMove(Control control, Point pt)
        {
            if (DesignMode || !mShowToggleButton)
                return false;
            Rectangle toggleRect = ToggleRect;
            FixedPanel hover = mToggleButtonHover;
            if (toggleRect.Contains(pt))
            {
                if (control == this)
                    mOverrideCursor.SetValue(this, null, null);
                if (Orientation == Orientation.Horizontal)
                {
                    if (pt.X < toggleRect.X + toggleRect.Width / 2)
                        mToggleButtonHover = FixedPanel.Panel1;
                    else
                        mToggleButtonHover = FixedPanel.Panel2;
                }
                else
                {
                    if (pt.Y < toggleRect.Y + toggleRect.Height / 2)
                        mToggleButtonHover = FixedPanel.Panel1;
                    else
                        mToggleButtonHover = FixedPanel.Panel2;
                }
            }
            else
                mToggleButtonHover = FixedPanel.None;
            if (mToggleButtonHover != hover)
                control.Invalidate();
            return (mToggleButtonHover != FixedPanel.None);
        }

        private void HandleMouseLeave(Control control)
        {
            if (mShowToggleButton)
            {
                mToggleButtonHover = FixedPanel.None;
                control.Invalidate();
            }
        }

        private void DrawToggleButton(Control control, Graphics g)
        {
            if (!mShowToggleButton)
                return;
            Rectangle rect = ToggleRect;
            Brush brush = Brushes.Red;
            if (Orientation == Orientation.Horizontal)
            {
                // 向下箭头
                if (control != this && Panel1Collapsed)
                {
                    if (mToggleButtonHover != FixedPanel.None)
                        g.FillRectangle(Brushes.LightGray, rect);
                    int x = rect.X + rect.Width / 2;
                    int y = rect.Bottom - 1;
                    int len = rect.Height - 1;
                    for (int i = 0; i < len; ++i)
                    {
                        g.FillRectangle(brush, x, y, i * 2 + 1, 1);
                        x--;
                        y--;
                    }
                }
                // 向上箭头
                else if (control != this && Panel2Collapsed)
                {
                    if (mToggleButtonHover != FixedPanel.None)
                        g.FillRectangle(Brushes.LightGray, rect);
                    int x = rect.X + rect.Width / 2;
                    int y = rect.Top;
                    int len = rect.Height - 1;
                    for (int i = 0; i < len; ++i)
                    {
                        g.FillRectangle(brush, x, y, i * 2 + 1, 1);
                        x--;
                        y++;
                    }
                }
                else if (control == this)
                {
                    if (mToggleButtonHover == FixedPanel.Panel1)
                        g.FillRectangle(Brushes.LightGray, rect.X, rect.Y, rect.Width / 2, rect.Height);
                    else if (mToggleButtonHover == FixedPanel.Panel2)
                        g.FillRectangle(Brushes.LightGray, rect.X + rect.Width / 2, rect.Y, rect.Width / 2, rect.Height);
                    //
                    // 向上箭头
                    //
                    int x = rect.X + rect.Width / 4;
                    int y = rect.Top;
                    int len = rect.Height - 1;
                    for (int i = 0; i < len; ++i)
                    {
                        g.FillRectangle(brush, x, y, i * 2 + 1, 1);
                        x--;
                        y++;
                    }
                    //
                    // 向下箭头
                    //
                    x = rect.X + 3 * rect.Width / 4;
                    y = rect.Bottom - 1;
                    len = rect.Height - 1;
                    for (int i = 0; i < len; ++i)
                    {
                        g.FillRectangle(brush, x, y, i * 2 + 1, 1);
                        x--;
                        y--;
                    }
                }
            }
            else
            {
                // 向右箭头
                if (control != this && Panel1Collapsed)
                {
                    if (mToggleButtonHover != FixedPanel.None)
                        g.FillRectangle(Brushes.LightGray, rect);
                    int x = rect.Right - 1;
                    int y = rect.Top + rect.Height / 2;
                    int len = rect.Width - 1;
                    for (int i = 0; i < len; ++i)
                    {
                        g.FillRectangle(brush, x, y, 1, i * 2 + 1);
                        x--;
                        y--;
                    }
                }
                // 向左箭头
                else if (control != this && Panel2Collapsed)
                {
                    if (mToggleButtonHover != FixedPanel.None)
                        g.FillRectangle(Brushes.LightGray, rect);
                    int x = rect.X;
                    int y = rect.Top + rect.Height / 2;
                    int len = rect.Width - 1;
                    for (int i = 0; i < len; ++i)
                    {
                        g.FillRectangle(brush, x, y, 1, i * 2 + 1);
                        x++;
                        y--;
                    }
                }
                else if (control == this)
                {
                    if (mToggleButtonHover == FixedPanel.Panel1)
                        g.FillRectangle(Brushes.LightGray, rect.X, rect.Y, rect.Width, rect.Height / 2);
                    else if (mToggleButtonHover == FixedPanel.Panel2)
                        g.FillRectangle(Brushes.LightGray, rect.X, rect.Y + rect.Height / 2, rect.Width, rect.Height / 2);
                    //
                    // 向左箭头
                    //
                    int x = rect.X;
                    int y = rect.Top + rect.Height / 4;
                    int len = rect.Width - 1;
                    for (int i = 0; i < len; ++i)
                    {
                        g.FillRectangle(brush, x, y, 1, i * 2 + 1);
                        x++;
                        y--;
                    }
                    //
                    // 向右箭头
                    //
                    x = rect.Right - 1;
                    y = rect.Top + 3 * rect.Height / 4;
                    len = rect.Width - 1;
                    for (int i = 0; i < len; ++i)
                    {
                        g.FillRectangle(brush, x, y, 1, i * 2 + 1);
                        x--;
                        y--;
                    }
                }
            }
        }

        private Rectangle ToggleRect
        {
            get
            {
                int borderSize = GetBorderSize();
                if (Orientation == Orientation.Horizontal)
                {
                    if (Panel1Collapsed)
                        return new Rectangle((Width - ToggleLength) / 2, 0, ToggleLength, SplitterWidth);
                    if (Panel2Collapsed)
                        return new Rectangle((Panel1.Width - ToggleLength) / 2, Panel1.Height - SplitterWidth - borderSize, ToggleLength, SplitterWidth);
                    return new Rectangle(SplitterRectangle.X + (SplitterRectangle.Width - ToggleLength) / 2, SplitterRectangle.Y, ToggleLength, SplitterRectangle.Height);
                }
                else
                {
                    if (Panel1Collapsed)
                        return new Rectangle(0, (Height - ToggleLength) / 2, SplitterWidth, ToggleLength);
                    if (Panel2Collapsed)
                        return new Rectangle(Panel1.Width - SplitterWidth - borderSize, (Panel1.Height - ToggleLength) / 2, SplitterWidth, ToggleLength);
                    return new Rectangle(SplitterRectangle.X, SplitterRectangle.Y + (SplitterRectangle.Height - ToggleLength) / 2, SplitterRectangle.Width, ToggleLength);
                }
            }
        }

        private int GetBorderSize()
        {
            int borderSize = 0;
            switch (BorderStyle)
            {
                case BorderStyle.FixedSingle:
                    borderSize = 1;
                    break;
                case BorderStyle.Fixed3D:
                    borderSize = 4;
                    break;
            }
            return borderSize;
        }

        private PropertyInfo GetPrivateProperty(string fieldname)
        {
            return typeof(System.Windows.Forms.SplitContainer).GetProperty(fieldname, BindingFlags.Instance | BindingFlags.NonPublic);
        }
    }
}
